﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace PullSdkHelper
{
    
    internal static class ZKSdk
    {
        internal static long GetValue(this IntPtr ptr)
        {
            if (IntPtr.Size == 4) // x86
                return ptr.ToInt32();

            return ptr.ToInt64(); // x64
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="date">值格式 20100203</param>
        /// <returns></returns>
        public static DateTime ConvertDate(int date)
        {
            if (date <= 19000101) return new DateTime(1900, 1, 1);
            var year = (date/10000);
            var monthAndDayPart = (date - year*10000);
            var month = monthAndDayPart/100;
            var day = monthAndDayPart%100;
            return new DateTime(year, month, day);
        }

        public static DateTime ConvertDatetime(int t)
        {
            var second = t%60;
            t /= 60;
            var minute = t%60;
            t /= 60;
            var hour = t%24;
            t /= 24;
            var day = t%31 + 1;
            t /= 31;
            var month = t%12 + 1;
            t /= 12;
            var year = t + 2000;
            return new DateTime(year, month, day, hour, minute, second);
        }

        public static int ConvertTimeInt(DateTime dt)
        {
            var t = dt.Year - 2000;
            var second = 1;
            var minute = 60 * second;
            var hour = 60 * minute;
            var day = 24 * hour;
            var month = 31 * day;
            var year = 12 * month;
            return dt.Second + dt.Minute * minute + dt.Hour * hour + (dt.Day - 1) * day + (dt.Month - 1) * month + t * year;
        }

        /// <summary>
        /// 连接设备，连接成功后返回连接句柄。 
        /// </summary>
        /// <param name="parameters">
        /// [in] 通过 Parameter 参数指定连接选项， 如下列例子：
        /// "protocol=RS485,port=COM2,baudrate=38400bps,deviceid=1,timeout=50000, passwd=”; 
        /// “protocol=TCP,ipaddress=192.168.12.154,port=4370,timeout=4000,passwd=”; 
        /// 需要向该函数传递与设备相关的连接参数方可实现连接功能。 
        /// protocol 是通讯使用的协议，目前有 RS485 和 TCP 两种方式； 
        /// port：设备通讯端口。例如，以RS485 方式连接，可设置port 为COM1；以 TCP通讯的端口，如 果不特别强调，port默认为4370； deviceid：串口使用的设备 RS485 通讯地址；
        /// baudrate：串口通讯使用的波特率； 
        /// ipaddress：TCP/IP 通讯相关设备的 IP 地址； 
        /// timeout：连接超时时间，单位为毫秒。遇到网络连接质量不好时，应加大 timeout 的值。一 般的，“timeout=5000”（5 秒）可以满足基本网络使用；当查询数据经常出现-2 错误码时，应加 大 timeout 的值，可以设置：“timeout=20000”（20 秒）。 
        /// passwd：设置通讯的连接密码，可以为空表示不使用密码。 
        /// （注：Parameters 连接字符串大小写敏感） 
        /// </param>
        /// <returns>与设备连接成功后返回该连接句柄，否则连接失败返回 0。 </returns>
        [DllImport("plcommpro.dll", EntryPoint = "Connect" )]
        public static extern IntPtr Connect(string parameters);

        /// <summary>
        /// 连接设备，连接成功后返回连接句柄，连接失败则通过*pErrorCode 来返回错误码。 [
        /// </summary>
        /// <param name="parameters">
        /// [in] 通过 Parameter 参数指定连接选项，如下列例子：
        ///  "protocol=RS485,port=COM2,baudrate=38400bps,deviceid=1,timeout=50000, passwd=”; 
        /// “protocol=TCP,ipaddress=192.168.12.154,port=4370,timeout=4000,passwd=”; 
        /// 需要向该函数传递与设备相关的连接参数方可实现连接功能。 
        /// protocol 是通讯使用的协议，目前有 RS485 和 TCP 两种方式； 
        /// port：设备通讯端口。例如，以RS485 方式连接，可设置port 为COM1；以 TCP通讯的端口，如 4. PullSDK 接口函数详述  - 3 - 果不特别强调，port默认为4370； 
        /// deviceid：串口使用的设备 RS485 通讯地址； 
        /// baudrate：串口通讯使用的波特率； 
        /// ipaddress：TCP/IP 通讯相关设备的 IP 地址； 
        /// timeout：连接超时时间，单位为毫秒。遇到网络连接质量不好时，应加大 timeout 的值。一 般的，“timeout=5000”（5 秒）可以满足基本网络使用；当查询数据经常出现-2 错误码时，应加 大 timeout 的值，可以设置：“timeout=20000”（20 秒）。 
        /// passwd：设置通讯的连接密码，可以为空表示不使用密码。 
        /// （注：Parameters 连接字符串大小写敏感） 
        /// </param>
        /// <param name="pErrorCode">[in] 通过 pErrorCode 参数来得到连接失败后的错误码，错误码说明见附表五。使用出参返 回改善了 LastError 机制的线程安全性。 </param>
        /// <returns>与设备连接成功后返回该连接句柄，否则连接失败返回 0</returns>
        [DllImport("plcommpro.dll", EntryPoint = "ConnectExt")]
        public static extern IntPtr ConnectExt(string parameters,int pErrorCode);


        /// <summary>
        /// 断开与设备的连接。 
        /// </summary>
        /// <param name="h">[in] 由 Connect()成功返回的句柄。 </param>
        [DllImport("plcommpro.dll", EntryPoint = "Disconnect")]
        public static extern void Disconnect(IntPtr h);

        /// <summary>
        /// 设置控制器参数，例如设备号、门磁类型、锁驱动时间、读卡间隔等。 
        /// </summary>
        /// <param name="h">[in] 由 Connect()成功返回的句柄。 </param>
        /// <param name="itemValues">[in] 将要设置的设备参数值，多个参数值之间可以用逗号分开，一次至多可同时设置 30 个 参数（可设置的参数值属性请参见表二）。 </param>
        /// <returns>返回为 0 时，表示成功；返回负数时为错误，错误代码的信息请参见附表五。 </returns>
        [DllImport("plcommpro.dll", EntryPoint = "SetDeviceParam")]
        public static extern IntPtr SetDeviceParam(IntPtr h,string itemValues);

        /// <summary>
        /// 读取控制器参数，例如设备号、门磁类型、锁驱动时间、读卡间隔等。 
        /// </summary>
        /// <param name="h">[in] 由 Connect()成功返回的句柄。 </param>
        /// <param name="buffer">[in] 用于接收返回数据的缓冲区，返回的数据是文本格式的，可能是多个参数值，各参数 之间用逗号分隔。</param>
        /// <param name="bufferSize">[in] 用于接收返回数据的缓冲区大小</param>
        /// <param name="items">[in]将要读取的设备参数名称表，多个名称之间可以用逗号分开，一次至多可同时读取 30 个参数（可读取的参数值属性请参见表一）。 </param>
        /// <returns>返回为 0 时，表示成功；返回负数时为错误，错误代码的信息请参见附表五。 </returns>
        [DllImport("plcommpro.dll", EntryPoint = "GetDeviceParam")]
        public static extern IntPtr GetDeviceParam(IntPtr h, byte[] buffer, int bufferSize,string items);

        /// <summary>
        /// 控制控制器动作。
        /// </summary>
        /// <param name="h">[in] 由 Connect()成功返回的句柄； </param>
        /// <param name="operationId">[in] 操作内容：1 表示锁输出或者辅助输出，2 表示取消报警，3 重启设备,4 启用和 禁用常开（这里的禁用常开包括首开常开、连续五次卡启用的常开、远程启用的常开）。 </param>
        /// <param name="param1">[in]  当 OperationID 为输出操作时，若 Param2 为门输出，此参数表示设备中门 的编号，若 Param2 为辅助输出，此参数表示设备中辅助输出口的编号，详细请参 见附表三；当 OperationID 为取消报警时，默认值为 0； </param>
        /// <param name="param2">[in] 当OperationID为输出操作时，此参数表示设备输出点地址类型（1：锁输出， 2： 辅助输出），详细请参见附表三；当OperationID为取消报警时，默认值为0；当 OperationID 为4，即常开时，该参数表示是常开启用还是禁用常开（0：禁用；1：启用）</param>
        /// <param name="param3">[in] 当 OperationID 为输出操作时，此参数表示开门时间（0 表示关，255 表示 常开，取值范围为 1~60（秒）），默认值为 0； </param>
        /// <param name="param4">[in] 预留之用，默认为 0； </param>
        /// <param name="options">[in] 默认为空，扩展之用； </param>
        /// <returns>返回为 0 或者正数时，表示成功；返回负数时表示失败，错误代码的信息请参 见附表五。 </returns>
        [DllImport("plcommpro.dll", EntryPoint = "ControlDevice")]
        public static extern IntPtr ControlDevice(IntPtr h, int operationId, int param1, int param2, int param3, int param4,string options);

        /// <summary>
        /// 设置数据到设备,用于设置时间段、用户信息、假日设置等数据，数据可以是一条记录，也 可以是多条记录，如果插入的记录的主键已在设备中则覆盖原记录。 
        /// </summary>
        /// <param name="h">[in] 由 Connect()成功返回的句柄； </param>
        /// <param name="tableName">[in] 数据表名，目前可使用表请参见附表四</param>
        /// <param name="data">[in] 数据记录表示，数据是文本格式的，多条记录之间用\r\n 分隔，各个“字段=值”对之 间用\t 分隔；  </param>
        /// <param name="options">[in] 默认为空，扩展之用； </param>
        /// <returns></returns>
        [DllImport("plcommpro.dll", EntryPoint = "SetDeviceData")]
        public static extern IntPtr SetDeviceData(IntPtr h, string tableName,string data,string options);

        /// <summary>
        /// 从设备读取数据,用于读取刷卡记录、时间段、用户信息、假日设置等数据，数据可以是一 条记录，也可以是多条记录。 
        /// </summary>
        /// <param name="h">[in] 由 Connect()成功返回的句柄； </param>
        /// <param name="buffer">[in] 用于接收返回数据的缓冲区，返回的数据是文本格式的，可能是多条记录，各条记录 之间用\r\n 分隔。 </param>
        /// <param name="bufferSize">[in] 用于接收返回数据的缓冲区大小</param>
        /// <param name="tableName">[in] 数据表名，目前可用的表名参见附表四； </param>
        /// <param name="fieldNames">[in] 字段名列表，多个字段之间用\t 分开，“*”表示全部字段，此时返回数据字段的第一 行是字段名</param>
        /// <param name="filter">[in] 读取数据的条件，当单独一个“字段名 操作符 值”构成的字符串时，可以支持多个 条件，使用逗号分隔，按如下方式： 字段名 >= （“ =”符号两边不可以有空格） </param>
        /// <param name="options">[in] 当前仅在下载门禁事件记录表的数据时有效，值为“New Record”时下载新记录，当为空 时下载全部记录。下载其他表数据时，该字段置为空字符串即可。 </param>
        /// <returns>返回为 0 或者正数时，表示操作成功，其值为记录条数；返回负数时为失败，错误代码的 信息请参见附表五。 </returns>
        [DllImport("plcommpro.dll", EntryPoint = "GetDeviceData")]
        public static extern IntPtr GetDeviceData(IntPtr h, byte[] buffer,int bufferSize,string tableName,string fieldNames,string filter,string options);

        /// <summary>
        /// 读取设备中的记录总数信息，返回指定数据的记录条数。 
        /// </summary>
        /// <param name="h">[in] 由 Connect()成功返回的句柄； </param>
        /// <param name="tableName">[in] 数据表名，目前可用的表名参见附表四； </param>
        /// <param name="filter">[in] 读取数据的条件，当单独一个“字段名 操作符 值”构成的字符串时，可以支持多个 条件，使用逗号分隔，按如下方式： 字段名 >= （“ =”符号两边不可以有空格） </param>
        /// <param name="options">[in] 当前仅在下载门禁事件记录表的数据时有效，值为“New Record”时下载新记录，当为空 时下载全部记录。下载其他表数据时，该字段置为空字符串即可。 </param>
        /// <returns>返回为 0 或者正数时，表示操作成功，其值为记录条数；返回负数时为失败，错误代码的 信息请参见附表五。 </returns>
        [DllImport("plcommpro.dll", EntryPoint = "GetDeviceDataCount")]
        public static extern IntPtr GetDeviceDataCount(IntPtr h, string tableName,string filter, string options);


        /// <summary>
        /// 删除设备中的数据，例如用户信息、时间段等数据。 
        /// </summary>
        /// <param name="h">[in] 由 Connect()成功返回的句柄； </param>
        /// <param name="tableName">[in] 数据表名，目前可用的表名参见附表四； </param>
        /// <param name="data">[in] 数据记录表示，数据是文本格式的，多条记录之间用\r\n 分隔，各个“字段=值”对之 间用\t 分隔；</param>
        /// <param name="options">默认为空 </param>
        /// <returns>返回为 0 或者正数时，表示操作成功，其值为记录条数；返回负数时为失败，错误代码的 信息请参见附表五。 </returns>
        [DllImport("plcommpro.dll", EntryPoint = "DeleteDeviceData")]
        public static extern IntPtr DeleteDeviceData(IntPtr h, string tableName, string data, string options);
        /// <summary>
        /// 获取设备产生的实时事件记录以及设备的门状态、报警状态等。 
        /// </summary>
        /// <param name="h">[in] 由 Connect()成功返回的句柄； </param>
        /// <param name="buffer">[in] 用于接收返回数据的缓冲区，返回的数据是文本格式的，可能是多条记录，各条记录 之间用\r\n 分隔。 </param>
        /// <param name="bufferSize">[in] 用于接收返回数据的缓冲区大小</param>
        /// <returns>返回为 0 或者正数时，表示操作成功，其值为记录条数；返回负数时为失败，错误代码的 信息请参见附表五。 </returns>
        [DllImport("plcommpro.dll", EntryPoint = "GetRTLog")]
        public static extern IntPtr GetRTLog(IntPtr h, byte[] buffer, int bufferSize);

        /// <summary>
        /// 获取设备产生的实时事件记录以及设备的门状态、报警状态等。不同于 GetRTLog，该函数 获取 PUSH 格式的记录
        /// </summary>
        /// <param name="h">[in] 由 Connect()成功返回的句柄； </param>
        /// <param name="buffer">[in] 用于接收返回数据的缓冲区，返回的数据是文本格式的，可能是多条记录，各条记录 之间用\r\n 分隔。 </param>
        /// <param name="bufferSize">[in] 用于接收返回数据的缓冲区大小</param>
        /// <returns>返回为 0 或者正数时，表示操作成功，其值为记录条数；返回负数时为失败，错误代码的 信息请参见附表五。 </returns>
        [DllImport("plcommpro.dll", EntryPoint = "GetRTLogExt")]
        public static extern IntPtr GetRTLogExt(IntPtr h, byte[] buffer, int bufferSize);

        /// <summary>
        /// 搜索局域网内的门禁控制器。 
        /// 由于该方法为 UDP 广播方式搜索局域网内的门禁控制器，UDP 广播包不能穿过路由器， 所以不能将控制器与服务器用路由器隔开。 
        /// 另外，对于设备和服务器不在同一个网段的情况，如果出现能通过该方法搜索到，但 Ping 不通控制器 IP 地址的情况，
        /// 请尝试调整控制器 IP 地址和服务器地址到同一个子网中（未必是 同一个网段），具体网络中的网络设置，可咨询相应的网管，以获取正确的 IP 地址
        /// ，子网掩码 以及网关。 [
        /// </summary>
        /// <param name="commType">[in] 通讯类型为 “UDP”（或者为 Ethernet），将会搜索指定通讯类型的设备； </param>
        /// <param name="address">[in] 广播地址，将会搜索指定 IP 地址范围内局域网的设备，默认为 255.255.255.255，即全 网广播； </param>
        /// <param name="buffer">[in] 用于存储搜索到的设备的缓冲区，用户应根据网内设备数量来确定申请内存值。例如 50 台设备以内建议申请 32K 的内存，100 台设备以内建议申请 64K 的内存大小。  </param>
        /// <returns>返回为 0 或者正数时，为搜索到的门禁控制器数量；返回负数时为失败，错误代码的信息 请参见附表五。 </returns>
        [DllImport("plcommpro.dll", EntryPoint = "SearchDevice")]
        public static extern IntPtr SearchDevice(string commType,string address,byte[] buffer);

        /// <summary>
        /// UDP 广播方式修改控制器 IP 地址（考虑到设备的安全性，只能修改没有设密码的控制器的 IP 地址，子网掩码和网关）。 
        /// 如果当前设备已经设置了设备通讯密码，为保证设备安全，将无法通过该方法来修改设备 的 IP 地址。
        /// 此时如需修改 IP 地址，请使用 SetDeviceParam 来设置 IP 地址、网关、子网掩码。 如果忘记通讯密码，
        /// 可使用拨码开关来进行通讯参数的初始化，通讯密码也将被取消。拨码开 关的第 7 位，默认为 OFF；
        /// 当在 10 秒内将其往上再往下拨动重复 3 次，最后拨回 OFF 位， 重启设备就可恢复 IP 地址、网关、子网掩码、通讯密码。 
        /// </summary>
        /// <param name="commType">[in] 搜索门禁控制器时采用的通讯方式。此处为“UDP”（或者为 Ethernet）； </param>
        /// <param name="address">[in] 广播地址，默认为 255.255.255.255； </param>
        /// <param name="buffer">[in] 用于存储目标设备的 MAC 地址、新 IP 地址； 除 IP 地址外，子网掩码和网关请根据当前网络进行设置</param>
        /// <returns>返回为 0 或者正数时，为接收数据的记录数；返回负数时为失败，错误代码的信息请参见 附表五。 </returns>
        [DllImport("plcommpro.dll", EntryPoint = "ModifyIPAddress")]
        public static extern IntPtr ModifyIPAddress(string commType, string address, byte[] buffer);

        /// <summary>
        /// 获取返回值错误码，用其他返回返回失败时，可通过该函数获取错误码。比如在使用函数 Connect()连接设备失败返回 0 时，可以使用该函数获取当前的错误码。 
        /// </summary>
        /// <returns>返回错误 ID 号。 </returns>
        [DllImport("plcommpro.dll", EntryPoint = "PullLastError")]
        public static extern IntPtr PullLastError();

        /// <summary>
        /// 将文件从 PC 传送到设备。 
        /// </summary>
        /// <param name="h">[in] 由 Connect()成功返回的句柄</param>
        /// <param name="fileName">[in] 传送到设备的文件名,例如 emfw.cfg 文件； </param>
        /// <param name="buffer">[in] 待传送文件的数据缓冲区； </param>
        /// <param name="bufferSize">[in] 传送数据的长度； </param>
        /// <param name="options">[in] 默认为空，扩展之用 </param>
        /// <returns>返回为 0 或者正数时，表示成功；返回负数时为失败，错误代码的信息请参见附表五。 </returns>
        [DllImport("plcommpro.dll", EntryPoint = "SetDeviceFileData")]
        public static extern IntPtr SetDeviceFileData(IntPtr h,string fileName,byte[] buffer,int bufferSize,string options);

        
        [DllImport("plcommpro.dll", EntryPoint = "GetDeviceFileData")]
        public static extern IntPtr GetDeviceFileData(IntPtr h, byte[] buffer, int bufferSize, string fileName, string options);


        [DllImport("plcommpro.dll", EntryPoint = "ProcessBackupData")]
        public static extern IntPtr ProcessBackupData(byte[] buffer, int bufferSize, byte[] outBuffer, int outBufferSize);
    }
}
